using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;

namespace HslControls.Charts
{
	/// <summary>
	/// 坐标轴类
	/// </summary>
	[TypeConverter(typeof(ExpandableObjectConverter))]
	public class Axis
	{
		/// <summary>
		/// 最小值缓存
		/// </summary>
		private double minValBuf;

		/// <summary>
		/// 最大值缓存
		/// </summary>
		private double maxValBuf;

		/// <summary>
		/// 标签各元素间的数据步长
		/// </summary>
		private double dataStep;

		/// <summary>
		/// 标签各元素间的坐标步长
		/// </summary>
		private float locationStep;

		/// <summary>
		/// 标签的最大宽度
		/// </summary>
		private float maxLabelWidth;

		/// <summary>
		/// 标签的最大高度
		/// </summary>
		private float maxLabelHeight;

		private HslChart _chart;

		private AxisType _type;

		private double _maxValueLimit = 1.0;

		private double _minValueLimit = 0.0;

		private List<AxisLabel> _labels = new List<AxisLabel>();

		private bool _isCustomLabels = false;

		private bool _visible = true;

		private bool _labelVisible = true;

		private Color _color = Color.FromArgb(51, 51, 51);

		private Brush _brush;

		private AxisPosition _position = AxisPosition.LeftBottom;

		private string _title;

		private bool _isReverse = false;

		private bool _autoAdapt = true;

		private bool _zoomEnabled = true;

		/// <summary>
		/// 图表对象
		/// </summary>
		[Browsable(false)]
		public HslChart Chart
		{
			get
			{
				return _chart;
			}
			internal set
			{
				_chart = value;
			}
		}

		/// <summary>
		/// 类型
		/// </summary>
		[Browsable(false)]
		public AxisType Type
		{
			get
			{
				return _type;
			}
			internal set
			{
				_type = value;
			}
		}

		/// <summary>
		/// 最大值极限
		/// </summary>
		[Category("HslChart")]
		[Description("最大值极限。")]
		[DefaultValue(1.0)]
		public double MaxValueLimit
		{
			get
			{
				return _maxValueLimit;
			}
			set
			{
				_maxValueLimit = value;
				MaxValue = value;
			}
		}

		/// <summary>
		/// 最小值极限
		/// </summary>
		[Category("HslChart")]
		[Description("最小值极限。")]
		[DefaultValue(0.0)]
		public double MinValueLimit
		{
			get
			{
				return _minValueLimit;
			}
			set
			{
				_minValueLimit = value;
				MinValue = value;
			}
		}

		/// <summary>
		/// 标签
		/// </summary>
		[Category("HslChart")]
		[Description("标签。")]
		[TypeConverter(typeof(CollectionConverter))]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
		public List<AxisLabel> Labels
		{
			get
			{
				return _labels;
			}
			private set
			{
				_labels = value;
			}
		}

		/// <summary>
		/// 是否使用自定义标签
		/// </summary>
		[Category("HslChart")]
		[Description("是否使用自定义标签。")]
		[DefaultValue(false)]
		public bool IsCustomLabels
		{
			get
			{
				return _isCustomLabels;
			}
			set
			{
				_isCustomLabels = value;
			}
		}

		/// <summary>
		/// 显示/隐藏
		/// </summary>
		[Category("HslChart")]
		[Description("显示/隐藏。")]
		[DefaultValue(true)]
		public bool Visible
		{
			get
			{
				return _visible;
			}
			set
			{
				_visible = value;
			}
		}

		/// <summary>
		/// 显示/隐藏标签，隐藏的标签不占用布局空间。
		/// </summary>
		[Category("HslChart")]
		[Description("显示/隐藏标签，隐藏的标签不占用布局空间。")]
		[DefaultValue(true)]
		public bool LabelVisible
		{
			get
			{
				return _labelVisible;
			}
			set
			{
				_labelVisible = value;
			}
		}

		/// <summary>
		/// 颜色
		/// </summary>
		[Category("HslChart")]
		[Description("颜色。")]
		[DefaultValue(true)]
		public Color Color
		{
			get
			{
				return _color;
			}
			set
			{
				_color = value;
				_brush = new SolidBrush(value);
			}
		}

		/// <summary>
		/// 位置
		/// </summary>
		[Category("HslChart")]
		[Description("位置。")]
		[DefaultValue(AxisPosition.LeftBottom)]
		public AxisPosition Position
		{
			get
			{
				return _position;
			}
			set
			{
				_position = value;
			}
		}

		/// <summary>
		/// 标题
		/// </summary>
		[Category("HslChart")]
		[Description("标题。")]
		[DefaultValue(null)]
		public string Title
		{
			get
			{
				return _title;
			}
			set
			{
				_title = value;
			}
		}

		/// <summary>
		/// 是否反转方向
		/// </summary>
		[Category("HslChart")]
		[Description("是否反转方向。")]
		[DefaultValue(false)]
		public bool IsReverse
		{
			get
			{
				return _isReverse;
			}
			set
			{
				_isReverse = value;
			}
		}

		/// <summary>
		/// 是否让数据范围自适应
		/// </summary>
		[Category("HslChart")]
		[Description("是否让数据范围自适应。")]
		[DefaultValue(true)]
		public bool AutoAdapt
		{
			get
			{
				return _autoAdapt;
			}
			set
			{
				_autoAdapt = value;
			}
		}

		/// <summary>
		/// 缩放使能
		/// </summary>
		[Category("HslChart")]
		[Description("缩放使能。")]
		[DefaultValue(true)]
		public bool ZoomEnabled
		{
			get
			{
				return _zoomEnabled;
			}
			set
			{
				_zoomEnabled = value;
			}
		}

		/// <summary>
		/// 宽度
		/// </summary>
		[Browsable(false)]
		public float Width
		{
			get;
			private set;
		}

		/// <summary>
		/// 高度
		/// </summary>
		[Browsable(false)]
		public float Height
		{
			get;
			private set;
		}

		/// <summary>
		/// 最大值
		/// </summary>
		[Browsable(false)]
		public double MaxValue
		{
			get;
			private set;
		} = 1.0;


		/// <summary>
		/// 最小值
		/// </summary>
		[Browsable(false)]
		public double MinValue
		{
			get;
			private set;
		} = 0.0;


		/// <summary>
		/// 数据范围
		/// </summary>
		[Browsable(false)]
		public double Range
		{
			get
			{
				double range = MaxValue - MinValue;
				if (range <= 0.0)
				{
					return 1.0;
				}
				return range;
			}
		}

		/// <summary>
		/// 缩放状态
		/// </summary>
		[Browsable(false)]
		public ZoomType ZoomState
		{
			get;
			private set;
		} = ZoomType.None;


		/// <summary>
		/// 实例化一个Axis对象
		/// </summary>
		public Axis()
		{
			_labels = new List<AxisLabel>();
			_brush = new SolidBrush(_color);
			maxLabelWidth = 0f;
			maxLabelHeight = 0f;
		}

		/// <summary>
		/// 重置数据范围
		/// </summary>
		internal void ResetRange()
		{
			MinValue = (minValBuf = _minValueLimit);
			MaxValue = (maxValBuf = _maxValueLimit);
		}

		/// <summary>
		/// 更新数据范围，通过<see cref="T:HslControls.Charts.SeriesBase" />对象数据的最大值及最小值进行更新。
		/// </summary>
		/// <remarks>当启用数据范围自适应，且不使用自定义标签时有效。</remarks>
		/// <param name="series">数据串对象</param>
		/// <param name="axisType">坐标轴类型</param>
		/// <param name="separatorSum">分隔器个数</param>
		internal void UpdateRange(SeriesBase series, AxisType axisType, int separatorSum)
		{
			if (_autoAdapt && !_isCustomLabels)
			{
				double? min = (axisType == AxisType.X) ? series.MinValueX : series.MinValueY;
				double? max = (axisType == AxisType.X) ? series.MaxValueX : series.MaxValueY;
				bool needUpdate = false;
				if (min.HasValue && minValBuf > min.Value)
				{
					minValBuf = min.Value;
					needUpdate = true;
				}
				if (max.HasValue && maxValBuf < max.Value)
				{
					maxValBuf = max.Value;
					needUpdate = true;
				}
				if (needUpdate)
				{
					UpdateRange(minValBuf, maxValBuf, separatorSum);
				}
			}
		}

		/// <summary>
		/// 更新缩放
		/// </summary>
		/// <param name="times">缩放倍数</param>
		internal void UpdateZoom(double times)
		{
			if (_zoomEnabled)
			{
				if (times > 0.0)
				{
					ZoomState = ZoomType.Enlarge;
				}
				else if (times < 0.0)
				{
					ZoomState = ZoomType.Reduce;
				}
				else
				{
					ZoomState = ZoomType.None;
				}
				if (times != 0.0)
				{
					double range = Range;
					MinValue += range * times;
					MaxValue -= range * times;
				}
			}
		}

		/// <summary>
		/// 更新标签，有多少个分割器就创建几个标签。
		/// </summary>
		/// <remarks>不使用自定义标签时有效</remarks>
		/// <param name="separatorSum">分隔器个数</param>
		internal void UpdateLabel(int separatorSum)
		{
			if (!_isCustomLabels)
			{
				_labels.Clear();
				dataStep = Range / (double)separatorSum;
				for (int i = 0; i <= separatorSum; i++)
				{
					double val = dataStep * (double)i + MinValue;
					_labels.Add(new AxisLabel(val));
				}
			}
		}

		/// <summary>
		/// 更新尺寸，计算X轴的高度或Y轴的宽度。
		/// </summary>
		/// <remarks>更新标签后调用</remarks>
		/// <param name="g">绘图对象</param>
		/// <param name="font">字体</param>
		internal void UpdateSize1(Graphics g, Font font)
		{
			if (_type == AxisType.X)
			{
				Height = CountAxisXHeight(g, font);
			}
			else if (_type == AxisType.Y)
			{
				Width = CountAxisYWidth(g, font);
			}
		}

		/// <summary>
		/// 更新尺寸，对X轴的宽度或Y轴的高度赋值。
		/// </summary>
		/// <remarks>计算完图表绘图区域后调用</remarks>
		/// <param name="border">图表绘图区域边界</param>
		/// <param name="separatorSum">分隔器个数</param>
		internal void UpdateSize2(Rectangle border, int separatorSum)
		{
			if (_type == AxisType.X)
			{
				Width = border.Width;
				locationStep = Width / (float)separatorSum;
			}
			else if (_type == AxisType.Y)
			{
				Height = border.Height;
				locationStep = Height / (float)separatorSum;
			}
		}

		/// <summary>
		/// 更新偏移量，对非自定义标签的值进行更新。
		/// </summary>
		/// <param name="separatorSum">分隔器个数</param>
		/// <param name="offset">偏移量</param>
		internal void UpdateOffset(int separatorSum, int offset)
		{
			if (_isCustomLabels)
			{
				return;
			}
			int times = (int)((float)offset / locationStep);
			double offsetVal = (double)times * dataStep;
			if (offsetVal != 0.0)
			{
				_labels.Clear();
				for (int i = 0; i <= separatorSum; i++)
				{
					double val = dataStep * (double)i + MinValue;
					val = (_isReverse ? (val + offsetVal) : (val - offsetVal));
					_labels.Add(new AxisLabel(val));
				}
			}
		}

		/// <summary>
		/// 绘制横向坐标轴
		/// </summary>
		/// <param name="g">绘图对象</param>
		/// <param name="font">字体</param>
		/// <param name="border">图表绘图区域边界</param>
		/// <param name="offset">偏移量</param>
		/// <param name="top">相对图表控件上侧距离</param>
		/// <param name="bottom">相对图表控件下侧距离</param>
		internal void DrawAxisX(Graphics g, Font font, Rectangle border, int offset, ref float top, ref float bottom)
		{
			if (!_visible)
			{
				return;
			}
			StringFormat titleSF = null;
			StringFormat labSF = null;
			float titleX = (float)border.Left + (float)border.Width / 2f;
			float titleY = 0f;
			float labX = _isCustomLabels ? ((float)(border.Left + offset)) : ((float)border.Left + (float)offset % locationStep);
			float labY = 0f;
			if (_position == AxisPosition.RightTop)
			{
				titleSF = AxisFormat.TitleFormatTopAxisX;
				labSF = AxisFormat.LabelFormatTopAxisX;
				titleY = top;
				labY = top + Height - 3f;
				top += Height;
			}
			else
			{
				titleSF = AxisFormat.TitleFormatBottomAxisX;
				labSF = AxisFormat.LabelFormatBottomAxisX;
				titleY = (float)border.Bottom + bottom + Height;
				labY = (float)border.Bottom + bottom + 3f;
				bottom += Height;
			}
			if (_labelVisible)
			{
				int labSum = _labels.Count;
				if (labSum > 0)
				{
					RectangleF rect = default(RectangleF);
					for (int i = 0; i < labSum; i++)
					{
						float x = 0f;
						if (_isCustomLabels)
						{
							double labValue = _isReverse ? _labels[labSum - i - 1].Value : _labels[i].Value;
							x = Convert.ToSingle((double)Width / Range * (labValue - MinValue));
							if (_isReverse)
							{
								x = Width - x;
							}
						}
						else
						{
							x = (float)i * locationStep;
						}
						x += labX;
						if (x >= (float)border.Left && x <= (float)border.Right)
						{
							PointF labLoca = new PointF(x, labY);
							if (!rect.Contains(labLoca))
							{
								string lab = _isReverse ? _labels[labSum - i - 1].Content : _labels[i].Content;
								g.DrawString(lab, font, _brush, labLoca, labSF);
								rect = new RectangleF(x, labY, maxLabelWidth + 21f, maxLabelHeight);
							}
						}
					}
				}
			}
			if (!string.IsNullOrEmpty(_title))
			{
				g.DrawString(_title, font, _brush, titleX, titleY, titleSF);
			}
		}

		/// <summary>
		/// 绘制纵向坐标轴
		/// </summary>
		/// <param name="g">绘图对象</param>
		/// <param name="font">字体</param>
		/// <param name="border">图表绘图区域边界</param>
		/// <param name="offset">偏移量</param>
		/// <param name="left">相对图表控件左侧距离</param>
		/// <param name="right">相对图表控件右侧距离</param>
		internal void DrawAxisY(Graphics g, Font font, Rectangle border, int offset, ref float left, ref float right)
		{
			if (!_visible)
			{
				return;
			}
			StringFormat titleSF = null;
			StringFormat labSF = null;
			float titleX = 0f;
			float titleY = border.Top + border.Height / 2;
			float labX = 0f;
			float labY = _isCustomLabels ? ((float)(border.Top + offset)) : ((float)border.Top + (float)offset % locationStep);
			if (_position == AxisPosition.RightTop)
			{
				titleSF = AxisFormat.TitleFormatRightAxisY;
				labSF = AxisFormat.LabelFormatRightAxisY;
				titleX = (float)border.Right + right + Width;
				labX = (float)border.Right + right + 3f;
				right += Width;
			}
			else
			{
				titleSF = AxisFormat.TitleFormatLeftAxisY;
				labSF = AxisFormat.LabelFormatLeftAxisY;
				titleX = left;
				labX = left + Width - 3f;
				left += Width;
			}
			if (_labelVisible)
			{
				int labSum = _labels.Count;
				if (labSum > 0)
				{
					RectangleF rect = default(RectangleF);
					for (int i = 0; i < labSum; i++)
					{
						float y = 0f;
						if (_isCustomLabels)
						{
							double labValue = _isReverse ? _labels[i].Value : _labels[labSum - i - 1].Value;
							y = Convert.ToSingle((double)Height / Range * (labValue - MinValue));
							if (!_isReverse)
							{
								y = Height - y;
							}
						}
						else
						{
							y = (float)i * locationStep;
						}
						y += labY;
						if (y >= (float)border.Top && y <= (float)border.Bottom)
						{
							PointF labLoca = new PointF(labX, y);
							if (!rect.Contains(labLoca))
							{
								string lab = _isReverse ? _labels[i].Content : _labels[labSum - i - 1].Content;
								g.DrawString(lab, font, _brush, labLoca, labSF);
								rect = new RectangleF(labX, y, maxLabelWidth, maxLabelHeight + 6f);
							}
						}
					}
				}
			}
			if (!string.IsNullOrEmpty(_title))
			{
				Helper.DrawString(g, _title, font, _brush, new PointF(titleX, titleY), titleSF, -90f);
			}
		}

		/// <summary>
		/// 更新数据范围，通过已知的最大值及最小值，计算并更新合适的数据范围。
		/// </summary>
		/// <param name="min">最小值</param>
		/// <param name="max">最大值</param>
		/// <param name="separatorSum">分隔器个数</param>
		private void UpdateRange(double min, double max, int separatorSum)
		{
			double range = max - min;
			double step = range / (double)separatorSum;
			double magnitude = Math.Pow(10.0, Math.Floor(Math.Log(step) / Math.Log(10.0)));
			double minimum = range / (double)separatorSum;
			double residual = minimum / magnitude;
			double tick = (residual > 5.0) ? (10.0 * magnitude) : ((residual > 2.0) ? (5.0 * magnitude) : ((!(residual > 1.0)) ? magnitude : (2.0 * magnitude)));
			MinValue = Math.Floor(min / tick) * tick;
			MaxValue = Math.Ceiling(max / tick) * tick;
		}

		/// <summary>
		/// 计算Y轴宽度
		/// </summary>
		/// <param name="g">绘图对象</param>
		/// <param name="font">字体</param>
		/// <returns>Y轴宽度</returns>
		private float CountAxisYWidth(Graphics g, Font font)
		{
			if (!_visible)
			{
				return 0f;
			}
			float titleWidth = 0f;
			if (!string.IsNullOrEmpty(_title))
			{
				SizeF size = g.MeasureString(_title, font);
				titleWidth = Helper.ConvertSize(size, -90f).Width + 3f;
			}
			if (!_labelVisible || _labels.Count < 1)
			{
				return titleWidth;
			}
			maxLabelHeight = 0f;
			maxLabelWidth = 0f;
			for (int i = 0; i < _labels.Count; i++)
			{
				SizeF labSize = g.MeasureString(_labels[i].Content, font);
				maxLabelHeight = ((labSize.Height > maxLabelHeight) ? labSize.Height : maxLabelHeight);
				maxLabelWidth = ((labSize.Width > maxLabelWidth) ? labSize.Width : maxLabelWidth);
			}
			return titleWidth + maxLabelWidth + 3f;
		}

		/// <summary>
		/// 计算X轴高度
		/// </summary>
		/// <param name="g">绘图对象</param>
		/// <param name="font">字体</param>
		/// <returns>X轴高度</returns>
		private float CountAxisXHeight(Graphics g, Font font)
		{
			if (!_visible)
			{
				return 0f;
			}
			float titleHeight = string.IsNullOrEmpty(_title) ? 0f : (g.MeasureString(_title, font).Height + 3f);
			if (!_labelVisible || _labels.Count < 1)
			{
				return titleHeight;
			}
			maxLabelHeight = 0f;
			maxLabelWidth = 0f;
			for (int i = 0; i < _labels.Count; i++)
			{
				SizeF labSize = g.MeasureString(_labels[i].Content, font);
				maxLabelHeight = ((labSize.Height > maxLabelHeight) ? labSize.Height : maxLabelHeight);
				maxLabelWidth = ((labSize.Width > maxLabelWidth) ? labSize.Width : maxLabelWidth);
			}
			return titleHeight + maxLabelHeight + 3f;
		}

		/// <summary>
		/// 设置自定义标签
		/// </summary>
		/// <param name="labels">字符串数组</param>
		/// <param name="startValue">在数据范围中的起始值</param>
		/// <param name="valueStep">字符串数组各元素间的步长</param>
		public void SetLabels(string[] labels, double startValue = 0.0, double valueStep = 0.1)
		{
			if (labels != null)
			{
				_isCustomLabels = true;
				_labels.Clear();
				for (int i = 0; i < labels.Length; i++)
				{
					_labels.Add(new AxisLabel(startValue + (double)i * valueStep)
					{
						Content = labels[i]
					});
				}
			}
			_chart?.Invalidate();
		}

		/// <summary>
		/// 设置自定义标签
		/// </summary>
		/// <remarks>字符串数组与值数组长度必须相等</remarks>
		/// <param name="labels">字符串数组</param>
		/// <param name="values">值数组</param>
		public void SetLabels(string[] labels, double[] values)
		{
			if (labels != null && values != null)
			{
				_isCustomLabels = true;
				_labels.Clear();
				for (int i = 0; i < labels.Length; i++)
				{
					_labels.Add(new AxisLabel(values[i])
					{
						Content = labels[i]
					});
				}
			}
			_chart?.Invalidate();
		}

		/// <summary>
		/// 设置自定义标签
		/// </summary>
		/// <param name="dateTimes">时间数组</param>
		/// <param name="format">时间格式</param>
		/// <param name="startValue">在数据范围中的起始值</param>
		/// <param name="valueStep">时间数组各元素间的步长</param>
		public void SetLabels(DateTime[] dateTimes, string format = "yyyy-MM-dd HH:mm:ss", double startValue = 0.0, double valueStep = 0.1)
		{
			if (dateTimes != null)
			{
				_isCustomLabels = true;
				_labels.Clear();
				for (int i = 0; i < dateTimes.Length; i++)
				{
					_labels.Add(new AxisLabel(startValue + (double)i * valueStep)
					{
						Content = dateTimes[i].ToString(format)
					});
				}
			}
			_chart?.Invalidate();
		}

		/// <summary>
		/// 设置自定义标签
		/// </summary>
		/// <remarks>时间数组与值数组长度必须相等</remarks>
		/// <param name="dateTimes">时间数组</param>
		/// <param name="format">格式化的文本信息</param>
		/// <param name="values">值数组</param>
		public void SetLabels(DateTime[] dateTimes, double[] values, string format = "yyyy-MM-dd HH:mm:ss")
		{
			if (dateTimes != null && values != null)
			{
				_isCustomLabels = true;
				_labels.Clear();
				for (int i = 0; i < dateTimes.Length; i++)
				{
					_labels.Add(new AxisLabel(values[i])
					{
						Content = dateTimes[i].ToString(format)
					});
				}
			}
			_chart?.Invalidate();
		}

		/// <summary>
		/// 获取值的标签
		/// </summary>
		/// <remarks>如果使用自定义标签的话，就在现有的标签里查找与输入值相等的标签。</remarks>
		/// <param name="value">值</param>
		/// <returns>标签</returns>
		public string GetLabel(double value)
		{
			if (_isCustomLabels)
			{
				IEnumerable<AxisLabel> lst = _labels.Where((AxisLabel lab) => lab.Value == value);
				if (lst != null && lst.Count() > 0)
				{
					return lst.Last().Content;
				}
				return string.Empty;
			}
			return value.ToString();
		}
	}
}
